Skip to main content

Two-Way Main-Renderer IPC

note

See the Electron-Documentation for a more indepth explanation of the reasons for, and how the IPC works between the Main process, the Renderer process, and why the preload step is required.

To Share Or Not To Share

Unlike typical Fable.Remoting, the entire stack is compiled to JavaScript, so there is no requirement to define your API types in a Shared project. You can choose to do this or not.

Example

The Two-Way Main-Renderer IPC channel allows the client/renderer process to send a message to the main process, and to then receive a response asynchronously.

This is required due to the Renderer processes not having access to the Node.js API, nor the full Electron API.

A full example is provided below:

open Fable.Core.JS // Promise type

type ExampleRouting = {
SayHelloWorld: string -> Promise<Result<string, unit>>
}
warning

The API for Fable.Electron.Remoting is highly likely to change according to user preference at the beginning.

IpcMainEvent

The shared contract should remain focused on renderer-callable arguments. If you need IpcMainEvent inside Main handlers, provide a factory function and use Remoting.fromIpcMainEvent.

important

Do not add IpcMainEvent to the shared record function signatures. Use explicit context injection on the Main side instead.

Shared.fs
type ExampleRouting = {
SayHelloWorld: string -> Promise<Result<string, unit>>
}
Renderer.fs
let api =
Remoting.createIpc ()
|> Remoting.buildProxySender<Shared.ExampleRouting>

(api.SayHelloWorld "hello").``then``(function
| Ok v -> console.log v
| Error _ -> console.log "Didn't say hello back :("
|> ignore
Main.fs
let api (event: IpcMainEvent) : Shared.ExampleRouting = {
SayHelloWorld = fun (text: string) -> promise {
if text = "hello" then
return Ok <| $"Window {event.sender.id} said: {text} world!"
else
return Error()
}
}

Remoting.createIpc ()
|> Remoting.fromIpcMainEvent api

If you do not need event context, keep using Remoting.fromValue.